#!/usr/bin/ruby

require 'fileutils'

RUBY_OPEN = "<%"
RUBY_CLOSE = "%>"

# keep variables separate from <% variables %> 
class SafeContext
  attr_accessor :finally
  def initialize (outputname)
    @finally = []
    @outputname = outputname
  end
end

# cleanup keeps track of .tmp files created for deletion later
cleanup = []

# use this to throw errors
def error (explaination)
  $stdout = STDOUT
  puts "error:"
  puts explaination
  puts "aborting"
  exit
end

# can't include standard lib without this... or i'd put it there.
def source (filename)
  instance_eval File.open(filename, "r").read
end

inputname = ARGV[0]
outputname = inputname[0..-3]

safe_context = SafeContext.new outputname

FileUtils.copy inputname, inputname + ".0.tmp"
cleanup << (inputname + ".0.tmp")

iter = 1
finished = false
until finished
  finished = true

  input = File.open(inputname + "." + (iter - 1).to_s + ".tmp", "r")

  # redirect stdout so <% puts %> and <% print %> don't just go to the screen
  $stdout = File.open(inputname + "." + iter.to_s + ".rb.tmp", "w")
  cleanup << (inputname + "." + iter.to_s + ".rb.tmp")

  in_ruby = false
  while line = input.gets
    chunks = line.split /(#{Regexp.escape(RUBY_OPEN)}[dD]*|#{Regexp.escape(RUBY_CLOSE)})/
      chunks.each do |chunk|
      if chunk == RUBY_OPEN
        # error "unexpected '#{RUBY_OPEN}' found. expecting '#{RUBY_CLOSE}'" if in_ruby
        in_ruby = true
        next 
      end

      if chunk == RUBY_CLOSE
        # error "unexpected '#{RUBY_CLOSE}' found. expecting: '#{RUBY_OPEN}'" unless in_ruby
        puts "print ' #{RUBY_CLOSE}'" unless in_ruby
        in_ruby = false
        next
      end

      if chunk.start_with? RUBY_OPEN
        finished = false
        puts "print '#{chunk.chop} '"
        next
      end

      if in_ruby
        puts chunk.strip
      else
        escaped = chunk.gsub("\\", "\\\\\\\\").gsub("'", "\\\\'").gsub("\n", "")
        puts "print '#{escaped}'"
      end
    end

    puts "puts" unless in_ruby
  end

  $stdout.close

  # execute the script
  script = File.open(inputname + "." + iter.to_s + ".rb.tmp", "r").read
  $stdout = File.open(inputname + "." + iter.to_s + ".tmp", "w")
  cleanup << (inputname + "." + iter.to_s + ".tmp")
  safe_context.instance_eval(script)

  $stdout.close

  iter += 1
end

# at the very end sometimes things need to happen
# you can accumulate lambdas into finally for this purpose
safe_context.finally.each do |l|
  l.call
end

$stdout = STDOUT
FileUtils.copy inputname + "." + (iter - 1).to_s + ".tmp", outputname

cleanup.each do |file|
  FileUtils.rm file
end
